package spring;

import java.util.Arrays;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@Component
public final class spring {
    /**
     * BeanFactory是容器的基础接口，我们平时使用的各种容器都是它的实现
     * BeanFactory 只是一个最基础的接口，
     * 我们需要选择一个合适的实现类——在实际工作中，更多情况下会用到
     * ApplicationContext 的各种实现。此处，我们使用 DefaultListableBeanFactory
     * 这个实现类，它并不关心配置的方式，XmlBeanDefinitionReader 能读取 XML 文件中的
     * 元数据，我们通过它加载 CLASSPATH 中的 beans.xml 文件，将其保存到
     * DefaultListableBeanFactory 中，随后就可以通过 BeanFactory 的 getBean() 方法取得对应的 Bean 了。
     *
     * 类名 说明
     * ClassPathXmlApplicationContext 从 CLASSPATH 中加载 XML 文件来配置
     * ApplicationContext
     * FileSystemXmlApplicationContext 从文件系统中加载 XML 文件来配置
     * ApplicationContext
     * AnnotationConfigApplicationContext 根据注解和 Java 类配置 ApplicationContext
     */

    /**
     * 容器初始化
     * 大致步骤如下
     * (1) 从 XML 文件、Java 类或其他地方加载配置元数据。
     * (2) 通过 BeanFactoryPostProcessor 对配置元数据进行一轮处理。
     * (3) 初始化 Bean 实例，并根据给定的依赖关系组装对象。
     * (4) 通过 BeanPostProcessor 对 Bean 进行处理，期间还会触发 Bean 被构造后的回调方法。
     */


    /**
     * spring 的生命周期
     * 1. 创建对象
     * 2. 配置对象（注入依赖）
     * 3. 初始化对象（回调 实现 InitializingBean 接口、@PostConstruct 注解） 创建Bean后的回调查询数据库初始化缓存
     * 4. 使用对象
     * 5. 销毁对象（回调 实现 DisposableBean 接口、@PreDestroy 注解）
     * 6. 容器关闭
     */


    /**
     * 在大部分情况下，我们的 Bean 感知不到 Spring 容器的存在，也无须感知。但总有那么一些
     * 场景中我们要用到容器的一些特殊功能，这时就可以使用 Spring Framework 提供的很多
     * Aware 接口，让 Bean 能感知到容器的诸多信息
     * 例如，如果希望在 Bean 中获取容器信息，可以通过如下两种方式：
     * <p>
     * 实现 BeanFactoryAware 或 ApplicationContextAware 接口；
     * 用 @Autowired 注解来注入 BeanFactory 或 ApplicationContext。
     */
    @Autowired
    private ApplicationContext applicationContext;

    private void print() {   // 获取容器中 Bean 的数量
        System.out.println(applicationContext.getBeanDefinitionCount());
        // 获取容器中所有 Bean 的名称
        System.out.println(Arrays.toString(applicationContext.getBeanDefinitionNames()));
        // 获取容器中某个 Bean
        System.out.println(applicationContext.getBean("beanName"));
        // 获取容器中某个 Bean 的类型
        System.out.println(applicationContext.getType("beanName"));
        // 获取容器中某个 Bean 的属性
        System.out.println(applicationContext.getBean("beanName").getClass());
        // 判断容器中是否包含某个 Bean
        System.out.println(applicationContext.containsBean("beanName"));
    }


    /**
     * 如果要在程序中使用线程，请不要自行创建 Thread，而应该尽可能考虑使用线程池，
     * 并且明确线程池的大小—不能无限制地创建线程。
     * 网上有这样的建议，对于 CPU 密集型的系统，要尽可能减少线程数，建议线程池大小配
     * 置为“CPU 核数 +1”；对于 IO 密集型系统，为了避免 CPU 浪费在等待 IO 上，建议
     * 线程池大小为“CPU 核数 ×2”。当然，这只是一个建议值，具体还是可以根据情况来
     * 做调整的。
     */


    /**
     * SpringAOP的底层原理
     *  创建容器ioc = new applicationContext()
     *  Spring 把所有的Bean进行创建，进行依赖注入
     *  Srping (new $$SrpingCGLB$$0()) ---->  ioc.getBean(UserService.class)  继承当前的类，从新实现当前类的方法super.
     *  当实现了AOP Spring会根据当前的bean创建动态代理对象
     *  把bean进行替换-->所以自动装配的类称为new $$SrpingCGLB$$0()
     */

    /**
     * 视频讲述了JDK动态代理和cglib动态代理的区别，以及Spring和Spring Boot中对它们的默认使用情况等内容，具体如下：
     * - **JDK动态代理的特点**：只能代理实现了接口的类，因为其生成的代理类采用实现目标类接口的方式，且该代理类不可见。
     * - **cglib动态代理的特点**：可以代理未实现接口的类，生成的代理类采用继承目标类的方式，因此目标类不能被标记为final，否则无法生成子类进行代理。
     * - **动态代理在业务开发中的使用情况**：平常的业务开发中用不到动态代理，主要在底层框架开发时使用，实际应用开发基于Spring AOP实现，Spring AOP会自动选择合适的动态代理。
     * - **Spring中动态代理的默认选择**：默认使用JDK动态代理，会先判断目标类是否实现接口，实现则用JDK动态代理，否则用cglib动态代理，且可通过配置改变。
     * - **Spring Boot中动态代理的默认选择**：默认使用cglib动态代理，因为Spring Boot整合了cglib，无需单独引入包，且可通过配置文件改变。
     * - **JDK和cglib动态代理的效率对比**：JDK8以后两者效率相差无几。生成代理类阶段，JDK动态代理稍快，因为它只生成一个代理类，而cglib会生成多个；调用阶段，JDK动态代理通过反射调用，cglib通过直接调用父类方式，JDK8对反射性能优化后，两者调用链深度相近。
     */
}
